package com.lightkits.wms.domain.allot.impl;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.lightkits.common.MessageUtil;
import com.lightkits.common.TransactionValidate;
import com.lightkits.common.db.entity.*;
import com.lightkits.common.db.entity.event.BdEvent;
import com.lightkits.common.db.entity.event.BdEventRecord;
import com.lightkits.framework.common.exception.LightKitsBizException;
import com.lightkits.framework.common.logging.Logger;
import com.lightkits.framework.common.logging.LoggerFactory;
import com.lightkits.framework.common.model.CheckResult;
import com.lightkits.framework.common.model.IResult;
import com.lightkits.framework.common.utility.json.JsonUtil;
import com.lightkits.framework.common.utility.object.ObjectUtil;
import com.lightkits.framework.common.utility.validate.IntegrityValidator;
import com.lightkits.wms.domain.allot.CreateDocParamsService;
import com.lightkits.wms.domain.allot.CreateEventParamsService;
import com.lightkits.wms.domain.allot.CreateOnhAndTagService;
import com.lightkits.wms.domain.allot.UseAllotParamsService;
import com.lightkits.wms.domain.commons.service.WmsCommonsService;
import com.lightkits.wms.domain.nontaskpickinglist.repo.WmRequestHeaderService;
import com.lightkits.wms.domain.nontaskpickinglist.repo.WmRequestLineService;
import com.lightkits.wms.domain.rawmatstorage.repo.WmExecuteLineService;
import com.lightkits.wms.domain.saveonhand.repo.BdEventRecordService;
import com.lightkits.wms.domain.saveonhand.repo.BdEventService;
import com.lightkits.wms.domain.stocktransfer.service.SubStockTransferDomain;
import com.lightkits.wms.domain.tagservice.UseTagParamsService;
import com.lightkits.wms.dto.allot.model.PickHeadDto;
import com.lightkits.wms.dto.allot.model.PickLineDto;
import com.lightkits.wms.dto.stocktransfer.model.SubStockViewDTO;
import com.lightkits.wms.dto.tagservice.model.TagViewDto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

/**
 * UseAllotSendParamsServiceImpl.
 * 调拨发出
 *
 * @author jing.wei@hand-china.com.
 * @date 2019/5/23 20:38
 */
@Service
public class UseAllotSendParamsServiceImpl implements UseAllotParamsService {
    private final WmsCommonsService wmsCommonsService;
    private static final Logger LOGGER = LoggerFactory
            .getLogger(UseAllotSendParamsServiceImpl.class);

    @Autowired
    public UseAllotSendParamsServiceImpl(WmsCommonsService wmsCommonsService,
                                         CreateDocParamsService docParamsService,
                                         CreateEventParamsService eventParamsService,
                                         CreateOnhAndTagService onhAndTagService,
                                         BdEventService bdEventService,
                                         BdEventRecordService bdEventRecordService,
                                         SubStockTransferDomain subStockTransferDomain,
                                         WmRequestHeaderService wmRequestHeaderService,
                                         WmRequestLineService wmRequestLineService,
                                         WmExecuteLineService wmExecuteLineService,
                                         UseTagParamsService useTagParamsService) {
        this.wmsCommonsService = wmsCommonsService;
        this.docParamsService = docParamsService;
        this.eventParamsService = eventParamsService;
        this.onhAndTagService = onhAndTagService;
        this.bdEventService = bdEventService;
        this.bdEventRecordService = bdEventRecordService;
        this.subStockTransferDomain = subStockTransferDomain;
        this.wmRequestHeaderService = wmRequestHeaderService;
        this.wmRequestLineService = wmRequestLineService;
        this.wmExecuteLineService = wmExecuteLineService;
        this.useTagParamsService = useTagParamsService;
    }

    private final UseTagParamsService useTagParamsService;
    private final BdEventService bdEventService;
    private final BdEventRecordService bdEventRecordService;
    private final SubStockTransferDomain subStockTransferDomain;
    private final WmRequestHeaderService wmRequestHeaderService;
    private final WmRequestLineService wmRequestLineService;
    private final WmExecuteLineService wmExecuteLineService;

    private final CreateDocParamsService docParamsService;
    private final CreateEventParamsService eventParamsService;
    private final CreateOnhAndTagService onhAndTagService;

    /**
     * 调拨发出参数校验
     *
     * @param headDto
     * @param lineDTOS
     * @return
     **/
    private IResult checkParams(PickHeadDto headDto, List<PickLineDto> lineDTOS) {
        try {
            //headDto参数必输校验
            IntegrityValidator.begin()
                    .validate(headDto.getOrgId(), "Organization Id")
                    .validate(headDto.getToOrganzationId(), "To Organization Id");
            //headDto参数有效性校验
            TransactionValidate check = TransactionValidate
                    .beginValidity(FndOrganizationBase.class);
            check.checkIdValidity(
                    ObjectUtil.toString(headDto.getToOrganzationId()),
                    "ORGANIZATION_ID");
            check = TransactionValidate
                    .beginValidity(FndOrganizationBase.class);
            check.checkIdValidity(
                    ObjectUtil.toString(headDto.getOrgId()),
                    "ORGANIZATION_ID");

            IResult result = IResult.fail(MessageUtil.getSysMessage("NO_OR_INVALID"));
            if (!ObjectUtil.isNullOrEmpty(headDto.getCreatorId())) {
                FndWorker worker = new FndWorker()
                        .selectById(headDto.getCreatorId());
                if (worker == null || worker.getOrganizationId()
                        .equals(headDto.getOrgId())) {
                    return result;
                }
            }
            if (!ObjectUtil.isNullOrEmpty(headDto.getRequestWorkerId())) {
                FndWorker worker = new FndWorker()
                        .selectById(headDto.getRequestWorkerId());
                if (worker == null || worker.getOrganizationId()
                        .equals(headDto.getOrgId())) {
                    return result;
                }
            }
        } catch (LightKitsBizException e) {
            return e.result();
        }
        //发出与接受组织不能相同的校验
        if (headDto.getOrgId().equals(headDto.getToOrganzationId())) {
            return IResult.fail(
                    MessageUtil
                            .getSysMessage("TO_ORG_CANNOT_SAME_WITH_FROM_ORG"));
        }
        //lineDto参数的必输校验
        checkLines(lineDTOS, headDto.getOrgId(), headDto.getToOrganzationId());
        return IResult.success();
    }

    /**
     * 参数校验
     *
     * @param lineDtos 领料单行参数
     * @param orgId    来源组织
     * @param toOrgId  目标组织
     */
    @Override
    public void checkLines(List<PickLineDto> lineDtos, String orgId, String toOrgId) {
        lineDtos.forEach(line -> {
            IntegrityValidator.begin()
                    .validate(line.getItemId(), "Item Id")
                    .validate(line.getUomCode(), "Uom Code")
                    .validate(line.getFromWarehouseId(), "From Warehouse")
                    .validate(line.getViaWarehouseId(), "Via Warehouse")
                    .validate(line.getToWarehouseId(), "To Warehouse")
                    .validate(line.getApplyQty(), "Apply Quantity")
                    .validate(line.getOwnerType(), "Owner Type");
            Pattern pattern = Pattern.compile("([1-9]\\d*\\.?\\d*)|(0\\.\\d*[1-9])");
            if (!pattern.matcher(line.getApplyQty()).matches()) {
                throw new LightKitsBizException("The Apply Quantity is invalid");
            }
            if (!ObjectUtil.isNullOrEmpty(line.getApplyWeight())
                    && !pattern.matcher(line.getApplyWeight()).matches()) {
                throw new LightKitsBizException("The Apply Quantity is invalid");
            }
            //有效性校验
            TransactionValidate check = TransactionValidate
                    .beginValidity(FndItemBase.class);
            check.checkIdValidity(
                    ObjectUtil.toString(line.getItemId()),
                    "ITEM_ID");
            //组织下有效性校验
            IResult result = IResult.fail(MessageUtil.getSysMessage("NO_OR_INVALID"));
            checkWareValid(line.getFromWarehouseId(), orgId);
            checkWareValid(line.getViaWarehouseId(), orgId);
            checkWareValid(line.getToWarehouseId(), toOrgId);
            checkWmAreaValid(line.getFromAreaId(), orgId);
            checkWmAreaValid(line.getViaAreaId(), orgId);
            checkWmAreaValid(line.getToAreaId(), toOrgId);
            if (!ObjectUtil.isNullOrEmpty(line.getTagCode())) {
                BdTag tag = new BdTag()
                        .selectOne(new EntityWrapper()
                                .eq(BdTag.TAG_CODE, line.getTagCode()));
                if (ObjectUtil.isNullOrEmpty(line.getFromAreaId())) {
                    if (tag == null
                            || line.getFromWarehouseId()
                            .equals(tag.getWarehouseId())) {
                        throw new LightKitsBizException(
                                MessageUtil.getSysMessage("NO_OR_INVALID"));
                    }
                } else {
                    if (tag == null
                            || line.getFromWarehouseId()
                            .equals(tag.getWarehouseId())
                            || line.getFromAreaId()
                            .equals(tag.getWmAreaId())) {
                        throw new LightKitsBizException(
                                MessageUtil.getSysMessage("NO_OR_INVALID"));
                    }
                }

            }
            //仓库货位校验
            result = wmsCommonsService
                    .checkLocationParams(
                            line.getToWarehouseId(), line.getToAreaId());
            if (!result.isSuccessful()) {
                throw new LightKitsBizException(result.getMessage());
            }
        });

    }

    /**
     * 仓库有效性
     *
     * @param warehouseId 仓库ID
     * @param orgId       组织ID
     * @return
     **/
    private void checkWareValid(String warehouseId, String orgId) {
        try {
            if (!ObjectUtil.isNullOrEmpty(warehouseId)) {
                FndWarehouse warehouse = new FndWarehouse()
                        .selectById(warehouseId);
                if (warehouse == null || warehouse.getOrganizationId()
                        .equals(orgId)) {
                    throw new LightKitsBizException(
                            MessageUtil.getSysMessage("NO_OR_INVALID"));
                }
            }
        } catch (Exception e) {
            throw new LightKitsBizException(
                    MessageUtil.getSysMessage("NO_OR_INVALID")
            );
        }
    }

    /**
     * 组织下货位有效性校验
     *
     * @param wmAreaId 货位Id
     * @param orgId    组织ID
     * @return
     **/
    private void checkWmAreaValid(String wmAreaId, String orgId) {
        try {
            if (!ObjectUtil.isNullOrEmpty(wmAreaId)) {
                FndWmArea wmArea = new FndWmArea()
                        .selectById(wmAreaId);
                if (wmArea == null || wmArea.getOrganizationId()
                        .equals(orgId)) {
                    throw new LightKitsBizException(
                            MessageUtil.getSysMessage("NO_OR_INVALID"));
                }
            }
        } catch (Exception e) {
            throw new LightKitsBizException(
                    MessageUtil.getSysMessage("NO_OR_INVALID")
            );
        }
    }

    /**
     * 调拨发出
     *
     * @param headDto
     * @param lineDtos
     * @return
     **/
    @Override
    @Transactional(rollbackFor = Exception.class)
    public CheckResult<Long> allotSend(PickHeadDto headDto, List<PickLineDto> lineDtos) {
        CheckResult<Long> checkRes = new CheckResult<>();
        checkRes.setSuccessful(true);
        try {
            IResult result = this.checkParams(headDto, lineDtos);
            if (!result.isSuccessful()) {
                return result.fill(checkRes);
            }
            //Create Bd Event
            BdEvent event = new BdEvent().selectById(headDto.getEventId());
            CheckResult checkResult = new CheckResult();
            if (ObjectUtil.isEmpty(headDto.getEventId())) {
                checkResult = eventParamsService.createEvent(headDto, "", "WM_ALLOT_OUT");
                if (!checkResult.isSuccessful() || checkResult.getData() == null) {
                    throw new LightKitsBizException(checkResult.getMessage());
                }
                event = (BdEvent) checkResult.getData();
                boolean eventFlag = bdEventService.insert(event);
                if (!eventFlag) {
                    throw new LightKitsBizException("Insert Event Failed");
                }
            }
            List<BdEventRecord> bdEventRecords = new ArrayList<>();
            //Create Wm_Request_Header
            checkResult = docParamsService.createRequestHeader(headDto);
            if (!checkResult.isSuccessful() || checkResult.getData() == null) {
                throw new LightKitsBizException(checkResult.getMessage());
            }
            WmRequestHeader requestHeader = (WmRequestHeader) checkResult.getData();
            boolean headerFlag = wmRequestHeaderService.insert(requestHeader);
            if (!headerFlag) {
                throw new LightKitsBizException("Insert Wm Request Header Failed");
            }
            //create header Record
            bdEventRecords.add(convertRecord(event.getId(),
                    "WM_REQUEST_HEADER",
                    requestHeader.getId(),
                    JsonUtil.toString(requestHeader)
            ));
            //create Wm_request_line
            checkResult = docParamsService.createRequestLines(lineDtos, requestHeader.getId());
            if (!checkResult.isSuccessful() || checkResult.getData() == null) {
                throw new LightKitsBizException(checkResult.getMessage());
            }
            List<WmRequestLine> lines = (List<WmRequestLine>) checkResult.getData();
            boolean linesFlag = wmRequestLineService.insertBatch(lines);
            if (!linesFlag) {
                throw new LightKitsBizException("Insert Wm Request Lines Failed");
            }
            //create lines Records
            for (WmRequestLine line : lines) {
                bdEventRecords.add(convertRecord(event.getId(),
                        "WM_REQUEST_LINE",
                        line.getId(),
                        JsonUtil.toString(line)));
            }
            //create Wm_Execute_Line
            checkResult = docParamsService.createExecuteLine(lines, requestHeader.getId(), event.getId());
            if (!checkResult.isSuccessful() || checkResult.getData() == null) {
                throw new LightKitsBizException(checkResult.getMessage());
            }
            List<WmExecuteLine> exeLines = (List<WmExecuteLine>) checkResult.getData();
            boolean exeLinesFlag = wmExecuteLineService.insertBatch(exeLines);
            if (!exeLinesFlag) {
                throw new LightKitsBizException("Insert Wm Execute Lines Failed");
            }
            boolean recordsFlag = bdEventRecordService.insertBatch(bdEventRecords);
            if (!recordsFlag) {
                throw new LightKitsBizException("Insert Records Failed");
            }
            //SubstockTransfer
            checkResult = onhAndTagService.createSubStockParam(headDto, lineDtos);
            if (!checkResult.isSuccessful() || checkResult.getData() == null) {
                throw new LightKitsBizException(checkResult.getMessage());
            }
            List<SubStockViewDTO> subStockViewDTOS =
                    (List<SubStockViewDTO>) checkResult.getData();
            for (SubStockViewDTO sub : subStockViewDTOS) {
                sub.setParentEventId(event.getId().toString());
                IResult iResult = subStockTransferDomain.subStockTransfer(sub);
                if (!iResult.isSuccessful()) {
                    throw new LightKitsBizException(iResult.getMessage());
                }
            }
            //TagChange
            checkResult = onhAndTagService.createTags(lines, event, "SEND", headDto.getOrgId());
            if (!checkResult.isSuccessful() || checkResult.getData() == null) {
                throw new LightKitsBizException(checkResult.getMessage());
            }
            List<TagViewDto> tagViewDTOS =
                    (List<TagViewDto>) checkResult.getData();
            IResult iResult = useTagParamsService.insertValue(tagViewDTOS);
            if (!iResult.isSuccessful()) {
                throw new LightKitsBizException(iResult.getMessage());
            }

            checkRes.setData(event.getId());
        } catch (LightKitsBizException e) {
            LOGGER.error("allot Send.error", e);
            throw new LightKitsBizException(e);
        }
        return checkRes;
    }

    /**
     * 事件快照转换
     *
     * @param event          事件ID
     * @param objectType     类型
     * @param objectId       事件对象ID
     * @param snapShopRecord 事件记录
     * @return
     **/
    private BdEventRecord convertRecord(Long event,
                                        String objectType,
                                        Long objectId,
                                        String snapShopRecord) {

        CheckResult<BdEventRecord> result = eventParamsService.createEventRecord(objectType,
                "",
                objectId,
                "",
                snapShopRecord);
        if (!result.isSuccessful() || result.getData() == null) {
            throw new LightKitsBizException(result.getMessage());
        }
        BdEventRecord record = result.getData();
        record.setEventId(event);
        return result.getData();


    }
}
